import { useMountedRef } from "hooks";
import { useCallback, useState } from "react";

interface State<D> {
  error: Error | null;
  data: D | null;
  stat: "idle" | "loading" | "error" | "success";
}

const defaultInitalState: State<null> = {
  stat: "idle",
  data: null,
  error: null,
};

export const useAsync = <D>(initialState?: State<D>) => {
  const [state, setState] = useState<State<D>>({
    ...defaultInitalState,
    ...initialState,
  });
  const mountedRef = useMountedRef();

  const setDate = useCallback((data: D) => {
    setState({ data, stat: "success", error: null });
  }, []);

  const setError = useCallback((error: Error) => {
    setState({ data: null, stat: "error", error });
  }, []);

  const run = useCallback(
    (promise: Promise<D>) => {
      if (!promise || !promise.then) {
        throw new Error("请传入promise");
      }

      setState((prev) => ({ ...prev, stat: "loading" }));

      return promise
        .then((result) => {
          if (mountedRef.current) setDate(result);
          return result;
        })
        .catch((error) => {
          setError(error);
          return Promise.reject(error);
        });
    },
    [mountedRef, setDate, setError]
  );

  const retry = useCallback(() => {}, []);

  const status = useCallback(() => {
    const result = {
      isIdle: false,
      isLoading: false,
      isError: false,
      isSuccess: false,
    };

    switch (state.stat) {
      case "idle":
        result.isIdle = true;
        break;
      case "loading":
        result.isLoading = true;
        break;
      case "error":
        result.isError = true;
        break;
      case "success":
        result.isSuccess = true;
        break;
      default:
        break;
    }

    return result;
  }, [state]);

  return {
    ...status(),
    run,
    setDate,
    setError,
    retry,
    ...state,
  };
};
